package com.ebo.httpcall4j.springboot3;

import com.ebo.httpcall4j.core.log.HttpCallLogService;
import com.ebo.httpcall4j.core.model.HttpCallRequest;
import com.ebo.httpcall4j.core.model.HttpCallResponse;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.ContentCachingResponseWrapper;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;

/**
 * 拦截器，用于在请求处理完成后记录HTTP调用日志。
 * 它从请求属性中获取HttpCallRequest，并构建HttpCallResponse，然后调用HttpCallLogService记录日志。
 */
public class HttpCallLogInterceptor implements HandlerInterceptor {

    private final HttpCallLogService logService;

    public HttpCallLogInterceptor(HttpCallLogService logService) {
        this.logService = logService;
    }


    /**
     * 在请求处理完成后（包括视图渲染后）被调用。
     *
     * @param request  HttpServletRequest对象。
     * @param response HttpServletResponse对象。
     * @param handler  处理器（控制器方法）。
     * @param ex       在请求处理期间抛出的异常（如果有）。
     * @throws Exception 如果发生错误。
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        HttpCallRequest callRequest = (HttpCallRequest) request.getAttribute("httpCallRequest");
        if (callRequest == null) {
            return;
        }

        callRequest.setBody(getRequestPayload(request));

        HttpCallResponse callResponse = new HttpCallResponse();
        boolean isSuccess;

        if (ex != null) {
            isSuccess = false;
            callResponse.setStatusCode(500);
            callResponse.setBody(ex.getMessage());
            callResponse.setOriginalBody(callResponse.getBody());
        } else {
            updateResponse(response, callResponse);
            isSuccess = callResponse.getStatusCode() >= 200 && callResponse.getStatusCode() < 300;
        }
        logService.log(null, callRequest, callResponse, isSuccess, null);
    }

    /**
     * 从HttpServletResponse构建HttpCallResponse对象。
     *
     * @param response     HttpServletResponse对象。
     * @param callResponse 要更新的HttpCallResponse对象。
     */
    private void updateResponse(HttpServletResponse response, HttpCallResponse callResponse) {
        if (response instanceof ContentCachingResponseWrapper) {
            ContentCachingResponseWrapper responseWrapper = (ContentCachingResponseWrapper) response;
            callResponse.setStatusCode(responseWrapper.getStatus());
            callResponse.setBody(getResponsePayload(responseWrapper));
            callResponse.setOriginalBody(callResponse.getBody());
            try {
                responseWrapper.copyBodyToResponse();
            } catch (Exception e) {
                // ignore
            }
        } else if (response != null) {
            callResponse.setStatusCode(response.getStatus());
        }
    }

    /**
     * 从响应中获取响应体内容。
     *
     * @param wrapper ContentCachingResponseWrapper对象。
     * @return 响应体内容的字符串。
     */
    private String getResponsePayload(ContentCachingResponseWrapper wrapper) {
        byte[] buf = wrapper.getContentAsByteArray();
        if (buf.length > 0) {
            try {
                return new String(buf, 0, buf.length, wrapper.getCharacterEncoding());
            } catch (UnsupportedEncodingException e) {
                return "[Unsupported Encoding]";
            }
        }
        return "";
    }

    /**
     * 从请求中获取请求体内容。
     *
     * @param request HttpServletRequest对象，应为ContentCachingRequestWrapper。
     * @return 请求体内容的字符串。
     */
    private String getRequestPayload(HttpServletRequest request) throws IOException {
        if (request instanceof ContentCachingRequestWrapper) {
            ContentCachingRequestWrapper wrappedRequest = (ContentCachingRequestWrapper) request;
            byte[] cachedContent = wrappedRequest.getContentAsByteArray();
            if (cachedContent.length > 0) {
                // 缓存已有数据，直接从缓存获取
                return new String(cachedContent, wrappedRequest.getCharacterEncoding());
            } else {
                // 缓存未生成，通过流读取（读取后会自动缓存）
                return inputStreamToString(request, request.getCharacterEncoding());
            }
        } else {
            // 非缓存包装类，只能从流读取（注意：只能读一次）
            return inputStreamToString(request, request.getCharacterEncoding());
        }
    }


    public static String inputStreamToString(HttpServletRequest request, String charsetName) throws IOException {
        // 获取 Servlet 输入流
        ServletInputStream inputStream = request.getInputStream();

        // 字节数组输出流，用于临时存储读取的字节
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

        // 缓冲区，建议使用 8192 字节（8KB）作为缓冲区大小，平衡性能和内存占用
        byte[] buffer = new byte[8192];
        int bytesRead;

        // 循环读取输入流中的数据到缓冲区
        while ((bytesRead = inputStream.read(buffer)) != -1) {
            // 将缓冲区中的数据写入输出流
            outputStream.write(buffer, 0, bytesRead);
        }

        // 将字节数组转换为字符串，指定 UTF-8 编码（避免默认编码导致的乱码问题）
        return outputStream.toString(charsetName);
    }


}